package edu.eece261.fall11.server;

import java.io.IOException;
import java.util.List;
import java.util.UUID;

import javax.jdo.PersistenceManager;
import javax.servlet.http.*;


import com.google.gson.Gson;
import com.google.gson.JsonParseException;

import edu.eece261.fall11.Messages;
import edu.eece261.fall11.Messages.ActionType;
import edu.eece261.fall11.Messages.MessageToClient;
import edu.eece261.fall11.Messages.MessageToServer;
import edu.eece261.fall11.Messages.ResponseType;
import edu.eece261.fall11.server.PMF;
import edu.eece261.fall11.server.ShopList;
import edu.eece261.fall11.server.User;
import edu.eece261.fall11.server.UserUUIDPair;

public class ShoppingListServerServlet extends HttpServlet {
	private static final long serialVersionUID = 1873681955649622228L;
	
	private PersistenceManager pm;

	public void doGet(HttpServletRequest request, HttpServletResponse response) {
		response.setContentType("text/plain");
		try {
			response.getWriter().println(
					"Shopping List Android does not respond to GET method.");
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	public void doPost(HttpServletRequest request, HttpServletResponse response) {

		MessageToClient messageToClient = null;
		MessageToServer messageToServer = null;

		String JSONMessageToServer = request
				.getParameter("JSONMessageToServer");

		if (JSONMessageToServer == null) {
			try {
				response.getWriter().println("Error - no MessageToServer.");
			} catch (IOException e) {
				e.printStackTrace();
			}
			return;
		}

		try {
			Gson gson = new Gson();
			messageToServer = gson.fromJson(JSONMessageToServer,
					MessageToServer.class);
		} catch (JsonParseException e) {
			try {
				response.getWriter().println("Error - parsing JSON.");
			} catch (IOException e2) {
				e2.printStackTrace();
			}
			return;
		}

		pm = PMF.get().getPersistenceManager();
		CleanseUUIDs();

		switch (messageToServer.getAction()) {
		case CREATEUSER:
			messageToClient = createUser(messageToServer);
			break;
		case LOGIN:
			messageToClient = logIn(messageToServer);
			break;
		case LOGOUT:
			messageToClient = logout(messageToServer);
			break;
		default:
			// check if user is logged-in
			if (isLoggedIn(messageToServer.getUsername(), messageToServer
					.getUuid())) {
				switch (messageToServer.getAction()) {
				case POLL:
					messageToClient = poll(messageToServer);
					break;
				case ADDLIST:
					messageToClient = addList(messageToServer);
					break;
				case ADDITEM:
					messageToClient = addItem(messageToServer);
					break;
				case JOINLIST:
					messageToClient = joinList(messageToServer);
					break;
				case LEAVELIST:
					messageToClient = leaveList(messageToServer);
					break;
				case FINDLISTS:
					messageToClient = findLists(messageToServer);
					break;
				case FINISHLIST:
					messageToClient = finishList(messageToServer);
					break;
				case DELETEITEM:
					messageToClient = deleteItem(messageToServer);
					break;
				case EDITITEM:
					messageToClient = editItem(messageToServer);
				}
			} else {
				messageToClient = new MessageToClient().setAction(
						messageToServer.getAction()).setResponse(
						ResponseType.FAILURENOTLOGGEDIN);
			}
		}

		pm.close();

		if (messageToClient == null) {
			messageToClient = new MessageToClient().setAction(
					messageToServer.getAction()).setResponse(
					Messages.ResponseType.FAILURE);
		}

		messageToClient.setUsername(messageToServer.getUsername());

		try {
			Gson gson = new Gson();
			response.getWriter().print(gson.toJson(messageToClient));
		} catch (JsonParseException e) {
			try {
				response.getWriter().println("Error - parsing JSON.");
			} catch (IOException e2) {
				e2.printStackTrace();
			}
			return;
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	@SuppressWarnings("unchecked")
	private boolean isLoggedIn(String user, String uuid) {
		PersistenceManager pm = PMF.get().getPersistenceManager();
		String query = "SELECT FROM " + UserUUIDPair.class.getName()
				+ " WHERE Username == '" + user + "' && uuid == '" + uuid + "'";
		List<UserUUIDPair> users = (List<UserUUIDPair>) pm.newQuery(query)
				.execute();

		if (users.isEmpty())
			return false;
		else {
			users.get(0).setTime(System.currentTimeMillis());
			return true;
		}
	}

	@SuppressWarnings("unchecked")
	private MessageToClient createUser(Messages.MessageToServer messageToServer) {
		MessageToClient messageToClient;
		String query = "SELECT FROM " + User.class.getName()
				+ " WHERE Username == '" + messageToServer.getUsername() + "'";
		List<User> users = (List<User>) pm.newQuery(query).execute();

		if (users.isEmpty()) {
			User uNew = new User(messageToServer.getUsername(), messageToServer
					.getPassword(), messageToServer.getName(), messageToServer.getEmail());
			pm.makePersistent(uNew);

			messageToClient = new MessageToClient().setResponse(
					ResponseType.SUCCESS).setUsername(
					messageToServer.getUsername()).setAction(
					ActionType.CREATEUSER);
		} else {
			messageToClient = new MessageToClient().setResponse(
					ResponseType.FAILURE).setUsername(
					messageToServer.getUsername()).setAction(
					ActionType.CREATEUSER);
		}
		return messageToClient;
	}

	@SuppressWarnings("unchecked")
	private MessageToClient logIn(Messages.MessageToServer messageToServer) {
		MessageToClient messageToClient;
		String query = "SELECT FROM " + User.class.getName()
				+ " WHERE Username == '" + messageToServer.getUsername() + "'";
		List<User> users = (List<User>) pm.newQuery(query).execute();

		if (users.isEmpty()) {
			messageToClient = new MessageToClient().setResponse(
					ResponseType.FAILUREUSER).setUsername(
					messageToServer.getUsername()).setAction(ActionType.LOGIN);
		} else if (users.get(0).getPassword().equals(
				messageToServer.getPassword())) {
			UUID userLogin = UUID.randomUUID();
			UserUUIDPair userUUID = new UserUUIDPair(messageToServer
					.getUsername(), userLogin.toString());
			pm.makePersistent(userUUID);
			messageToClient = new MessageToClient().setResponse(
					ResponseType.SUCCESS).setUsername(
					messageToServer.getUsername()).setAction(ActionType.LOGIN)
					.setUuid(userLogin.toString());
		} else {
			messageToClient = new MessageToClient().setResponse(
					ResponseType.FAILUREPASS).setUsername(
					messageToServer.getUsername()).setAction(ActionType.LOGIN);
		}
		return messageToClient;
	}

	@SuppressWarnings("unchecked")
	private MessageToClient logout(Messages.MessageToServer messageToServer) {
		MessageToClient messageToClient;
		String query = "SELECT FROM " + UserUUIDPair.class.getName()
				+ " WHERE user == '" + messageToServer.getUsername()
				+ "' && uuid == '" + messageToServer.getUuid() + "'";

		List<UserUUIDPair> users = (List<UserUUIDPair>) pm.newQuery(query)
				.execute();

		if (users.size() > 0) {
			for (int i = 0; i < users.size(); i++) {
				pm.deletePersistent(users.get(i));
			}
			messageToClient = new MessageToClient()
					.setAction(ActionType.LOGOUT).setResponse(
							ResponseType.SUCCESS);
		} else {
			messageToClient = new MessageToClient()
					.setAction(ActionType.LOGOUT).setResponse(
							ResponseType.FAILURENOTLOGGEDIN);
		}
		return messageToClient;
	}

	@SuppressWarnings("unchecked")
	private MessageToClient poll(Messages.MessageToServer messageToServer) {
		MessageToClient messageToClient;
		messageToClient = new MessageToClient().setAction(ActionType.POLL)
				.setLastModified(System.currentTimeMillis()).setResponse(
						ResponseType.SUCCESS);

		List<ShopList> changedLists = (List<ShopList>) pm.newQuery(
				"SELECT FROM " + ShopList.class.getName()
						+ " WHERE listFollowers == '"
						+ messageToServer.getUsername() + "' && "
						+ "listTimestamp > "
						+ messageToServer.getLastModifiedTime()).execute();

		for (int i = 0; i < changedLists.size(); i++) {
			messageToClient.addList(changedLists.get(i).getMessagesList());
		}

		return messageToClient;
	}

	@SuppressWarnings("unchecked")
	private MessageToClient addList(Messages.MessageToServer messageToServer) {
		MessageToClient messageToClient = new MessageToClient();

		List<ShopList> lists = (List<ShopList>) pm.newQuery(
				"SELECT FROM " + ShopList.class.getName()
						+ " WHERE listTitle == '" + messageToServer.getTitle()
						+ "'").execute();
		if (lists.size() > 0) {
			messageToClient.setAction(ActionType.ADDLIST).setUsername(
					messageToServer.getUsername()).setResponse(
					ResponseType.FAILUREEXISTS);
			return messageToClient;
		}
		messageToClient.setAction(ActionType.ADDLIST).setUsername(
				messageToServer.getUsername())
				.setResponse(ResponseType.SUCCESS);
		Messages.ShopList msgList = new Messages.ShopList();
		msgList.setFinished(0).addFollower(messageToServer.getUsername())
				.setOwner(messageToServer.getUsername()).setTitle(
						messageToServer.getTitle()).setPassword(messageToServer.getListPassword());
		ShopList newList = new ShopList(msgList, true);
		pm.makePersistent(newList);
		messageToClient.addList(msgList.setGlobal_id(newList
				.getListGlobalID()));
		return messageToClient;
	}

	@SuppressWarnings("unchecked")
	private MessageToClient addItem(Messages.MessageToServer messageToServer) {
		MessageToClient messageToClient = new MessageToClient();
		List<ShopList> lists = (List<ShopList>) pm.newQuery(
				"SELECT FROM " + ShopList.class.getName()
						+ " WHERE listTitle == '" + messageToServer.getTitle()
						+ "'").execute();
		if (lists.isEmpty()) {
			Messages.ShopList msgList = new Messages.ShopList();
			msgList.setFinished(0).addFollower(messageToServer.getUsername())
					.setOwner(messageToServer.getUsername()).setTitle(
							messageToServer.getTitle()).addItem(
							messageToServer.getItem(),
							messageToServer.getUsername(),
							System.currentTimeMillis());

			ShopList newList = new ShopList(msgList, true);
			pm.makePersistent(newList);
			messageToClient.addList(
					msgList.setGlobal_id(newList.getListGlobalID()))
					.setAction(ActionType.ADDITEM).setUsername(
							messageToServer.getUsername()).setResponse(
							ResponseType.SUCCESS);
			return messageToClient;
		} else {
			if (lists.get(0).isListFinished() == 0) {
				Messages.ShopList msgList = new Messages.ShopList();
				msgList.setFinished(0)
						.setOwner(lists.get(0).getListOwner()).setTitle(
								lists.get(0).getListTitle()).setGlobal_id(
								lists.get(0).getListGlobalID())
						.setItems(lists.get(0).getListItems())
						.setUpdaters(lists.get(0).getListUpdaters())
						.setTimestamps(lists.get(0).getListTimestamps())
						.setFollowers(lists.get(0).getListFollowers())
						.addItem(messageToServer.getItem(),
								messageToServer.getUsername(),
								System.currentTimeMillis()).addFollower(
								messageToServer.getUsername());

				ShopList newList = new ShopList(msgList, false);
				pm.makePersistent(newList);
				pm.deletePersistent(lists.get(0));
				messageToClient.setAction(ActionType.ADDLIST).setUsername(
						messageToServer.getUsername()).setResponse(
						ResponseType.SUCCESS).addList(msgList);
				return messageToClient;
			} else {
				messageToClient.setAction(ActionType.ADDITEM).setUsername(
						messageToServer.getUsername()).setResponse(
						ResponseType.FAILUREFINISHED);
				return messageToClient;
			}
		}
	}

	@SuppressWarnings("unchecked")
	private MessageToClient joinList(MessageToServer messageToServer) {
		MessageToClient messageToClient = new MessageToClient();
		List<ShopList> lists = (List<ShopList>) pm.newQuery(
				"SELECT FROM " + ShopList.class.getName()
						+ " WHERE listTitle == '" + messageToServer.getTitle()
						+ "'").execute();

		if (lists.isEmpty()) {
			messageToClient.setAction(ActionType.JOINLIST).setResponse(
					ResponseType.FAILURE).setUsername(
					messageToServer.getUsername());
			return messageToClient;
		}

		lists.get(0).addListFollower(messageToServer.getUsername());
		ShopList newList = new ShopList(lists.get(0));
		pm.makePersistent(newList);
		pm.deletePersistent(lists.get(0));

		Messages.ShopList msgList = new Messages.ShopList();
		msgList.setFinished(0).setOwner(newList.getListOwner()).setTitle(
				newList.getListTitle()).setGlobal_id(
				newList.getListGlobalID()).setItems(
				newList.getListItems()).setUpdaters(
				newList.getListUpdaters()).setTimestamps(
				newList.getListTimestamps()).setFollowers(
				newList.getListFollowers());

		messageToClient.setAction(ActionType.JOINLIST).setResponse(
				ResponseType.SUCCESS)
				.setUsername(messageToServer.getUsername()).addList(msgList);

		return messageToClient;
	}

	@SuppressWarnings("unchecked")
	private MessageToClient findLists(MessageToServer messageToServer) {
		MessageToClient messageToClient = new MessageToClient();
		messageToClient.setAction(ActionType.FINDLISTS);
		messageToClient.setUsername(messageToServer.getUsername());
		List<ShopList> lists = (List<ShopList>) pm.newQuery(
				"SELECT FROM " + ShopList.class.getName()
						+ " WHERE listFinished != -1").execute();

		int count = 0;

		if (lists.isEmpty()) {
			messageToClient.setResponse(ResponseType.FAILURE);
			return messageToClient;
		}
		for (int j = 0 + messageToServer.getOffset(); j < lists.size(); ++j) {
			if (!lists.get(j).getListFollowers().contains(
					messageToServer.getUsername())) {
				messageToClient.addList(lists.get(j).getMessagesList());
				count++;
				if (count >= 10) {
					messageToClient.setResponse(ResponseType.SUCCESS);
					return messageToClient;
				}
			}
		}
		messageToClient.setResponse(ResponseType.SUCCESS);
		return messageToClient;

	}
	
	@SuppressWarnings("unchecked")
	private MessageToClient editItem(MessageToServer messageToServer) {
		MessageToClient messageToClient = new MessageToClient();
		List<ShopList> lists = (List<ShopList>) pm.newQuery(
				"SELECT FROM " + ShopList.class.getName()
				+ " WHERE listTitle == '" + messageToServer.getTitle()
				+ "'").execute();
		if (lists.isEmpty()) {
			messageToClient.setAction(ActionType.EDITITEM).setUsername(
					messageToServer.getUsername()).setResponse(
					ResponseType.FAILURENOTPERMITTED);
			return messageToClient;
		} 
		
		int index = lists.get(0).getListTimestamps().indexOf(messageToServer.getItemTimestamp());
		if (index < 0)
		{
			messageToClient.setAction(ActionType.EDITITEM).setUsername(
					messageToServer.getUsername()).setResponse(
					ResponseType.FAILURE);
		}
		
		lists.get(0).editListItem(index, messageToServer.getItem(),messageToServer.getUsername());
		
		ShopList newList = new ShopList(lists.get(0));
		Messages.ShopList msgList = new Messages.ShopList();
		msgList.setFinished(0).setOwner(newList.getListOwner())
				.setTitle(newList.getListTitle()).setGlobal_id(
						newList.getListGlobalID()).setItems(
						newList.getListItems()).setFollowers(
						newList.getListFollowers());
		messageToClient.setAction(ActionType.EDITITEM).setUsername(
				messageToServer.getUsername()).setResponse(
				ResponseType.SUCCESS).addList(msgList);
		return messageToClient;
	}
	
	@SuppressWarnings("unchecked")
	private MessageToClient deleteItem(MessageToServer messageToServer) {
		MessageToClient messageToClient = new MessageToClient();
		List<ShopList> lists = (List<ShopList>) pm.newQuery(
				"SELECT FROM " + ShopList.class.getName()
				+ " WHERE listTitle == '" + messageToServer.getTitle()
				+ "'").execute();
		if (lists.isEmpty()) {
			messageToClient.setAction(ActionType.DELETEITEM).setUsername(
					messageToServer.getUsername()).setResponse(
					ResponseType.FAILURENOTPERMITTED);
			return messageToClient;
		}
		int index = lists.get(0).getListTimestamps().indexOf(messageToServer.getItemTimestamp());
		if (index < 0)
		{
			messageToClient.setAction(ActionType.DELETEITEM).setUsername(
					messageToServer.getUsername()).setResponse(
					ResponseType.FAILURE);
		}
		
		lists.get(0).getListTimestamps().remove(index);
		lists.get(0).getListItems().remove(index);
		lists.get(0).getListUpdaters().remove(index);
		lists.get(0).setListTimestamp(System.currentTimeMillis());
		
		ShopList newList = new ShopList(lists.get(0));
		Messages.ShopList msgList = new Messages.ShopList();
		msgList.setFinished(0).setOwner(newList.getListOwner())
				.setTitle(newList.getListTitle()).setGlobal_id(
						newList.getListGlobalID()).setItems(
						newList.getListItems()).setFollowers(
						newList.getListFollowers());
		messageToClient.setAction(ActionType.DELETEITEM).setUsername(
				messageToServer.getUsername()).setResponse(
				ResponseType.SUCCESS).addList(msgList);
		return messageToClient;
	}

	@SuppressWarnings("unchecked")
	private MessageToClient leaveList(MessageToServer messageToServer) {
		MessageToClient messageToClient = new MessageToClient();
		List<ShopList> lists = (List<ShopList>) pm.newQuery(
				"SELECT FROM " + ShopList.class.getName()
						+ " WHERE listTitle == '" + messageToServer.getTitle()
						+ "'").execute();
		if (lists.isEmpty()) {
			messageToClient.setAction(ActionType.LEAVELIST).setUsername(
					messageToServer.getUsername()).setResponse(
					ResponseType.FAILURENOTPERMITTED);
			return messageToClient;
		}
		if (lists.get(0).getListFollowers().remove(
				messageToServer.getUsername())) {
			ShopList newList = new ShopList(lists.get(0));
			if (newList.getListOwner().equals(messageToServer.getUsername())) {
				newList.setListFinished(-1);
			}
			if (!newList.getListFollowers().isEmpty()) {
				pm.makePersistent(newList);
			}
			pm.deletePersistent(lists.get(0));
			Messages.ShopList msgList = new Messages.ShopList();
			msgList.setFinished(0).setOwner(newList.getListOwner())
					.setTitle(newList.getListTitle()).setGlobal_id(
							newList.getListGlobalID()).setItems(
							newList.getListItems()).setFollowers(
							newList.getListFollowers());

			messageToClient.setAction(ActionType.LEAVELIST).setUsername(
					messageToServer.getUsername()).setResponse(
					ResponseType.SUCCESS).addList(msgList);
			return messageToClient;
		} else {
			messageToClient.setAction(ActionType.LEAVELIST).setUsername(
					messageToServer.getUsername()).setResponse(
					ResponseType.FAILURE);
			return messageToClient;
		}
	}

	@SuppressWarnings("unchecked")
	private MessageToClient finishList(MessageToServer messageToServer) {
		MessageToClient messageToClient = new MessageToClient().setAction(
				ActionType.FINISHLIST).setUsername(
				messageToServer.getUsername());
		List<ShopList> lists = (List<ShopList>) pm.newQuery(
				"SELECT FROM " + ShopList.class.getName()
						+ " WHERE listTitle == '" + messageToServer.getTitle()
						+ "'").execute();
		if (lists.isEmpty()) {
			messageToClient.setResponse(ResponseType.FAILURE);
			return messageToClient;
		}
		if (lists.get(0).getListOwner()
				.equals(messageToServer.getUsername())) {
			ShopList newList = new ShopList(lists.get(0));
			newList.setListFinished(1);
			pm.makePersistent(newList);
			pm.deletePersistent(lists.get(0));
			messageToClient.setResponse(ResponseType.SUCCESS);
			return messageToClient;
		} else {
			messageToClient.setResponse(ResponseType.FAILURENOTPERMITTED);
			return messageToClient;
		}
	}

	@SuppressWarnings("unchecked")
	private void CleanseUUIDs() {
		pm = PMF.get().getPersistenceManager();

		// Logins only remain valid for 15 minutes
		long compareTime = System.currentTimeMillis() - (60000 * 15);

		String query = "SELECT FROM " + UserUUIDPair.class.getName()
				+ " WHERE time < " + compareTime;

		List<UserUUIDPair> users = (List<UserUUIDPair>) pm.newQuery(query)
				.execute();

		for (int i = 0; i < users.size(); i++) {
			pm.deletePersistent(users.get(i));
		}
	}

}